package main

import (
	"fmt"
	"strings"
)

/*
与string值相比，strings.Builder类型的值有哪些优势？
	strings.Builder类型的值（以下简称Builder值）的优势有下面的三种：

	已存在的内容不可变，但可以拼接更多的内容；
	减少了内存分配和内容拷贝的次数；
	可将内容重置，可重用值。

	在 Go 语言中，string类型的值是不可变的。 如果想获得一个不一样的字符串，那么就只能基于原字符串进行裁剪、拼接等操作，从而生成一个新的字符串。

	在底层，一个string值的内容会被存储到一块连续的内存空间中。同时，这块内存容纳的字节数量也会被记录下来，并用于表示该string值的长度。

	在进行字符串拼接的时候，Go 语言会把所有被拼接的字符串依次拷贝到一个崭新且足够大的连续内存空间中，并把持有相应指针值的string值作为结果返回。

与string值相比，Builder值的优势其实主要体现在字符串拼接方面。

	Builder值中有一个用于承载内容的容器（以下简称内容容器）。它是一个以byte为元素类型的切片（以下简称字节切片）。

	已存在于Builder值中的内容是不可变的。因此，可以利用Builder值提供的方法拼接更多的内容，而丝毫不用担心这些方法会影响到已存在的内容。
		Builder值拥有的一系列指针方法，包括：Write、WriteByte、WriteRune和WriteString。可以把它们统称为拼接方法。

	可以通过调用上述方法把新的内容拼接到已存在的内容的尾部（也就是右边）。这时，如有必要，Builder值会自动地对自身的内容容器进行扩容。这里的自动扩容策略与切片的扩容策略一致。

	在向Builder值拼接内容的时候并不一定会引起扩容。只要内容容器的容量够用，扩容就不会进行，针对于此的内存分配也不会发生。同时，只要没有扩容，Builder值中已存在的内容就不会再被拷贝。

	除了Builder值的自动扩容，还可以选择手动扩容，这通过调用Builder值的Grow方法就可以做到。Grow方法也可以被称为扩容方法，它接受一个int类型的参数n，该参数用于代表将要扩充的字节数量。


问题 1：strings.Builder类型在使用上有约束吗？

	有约束，概括如下：

	在已被真正使用后就不可再被复制；
	由于其内容不是完全不可变的，所以需要使用方自行解决操作冲突和并发安全问题。

问题 2：为什么说strings.Reader类型的值可以高效地读取字符串？
	与strings.Builder类型恰恰相反，strings.Reader类型是为了高效读取字符串而存在的。后者的高效主要体现在它对字符串的读取机制上，它封装了很多用于在string值上读取内容的最佳实践。
	strings.Reader类型的值（以下简称Reader值）可以很方便地读取一个字符串中的内容。在读取的过程中，Reader值会保存已读取的字节的计数（以下简称已读计数）。
	已读计数也代表着下一次读取的起始索引位置。Reader值正是依靠这样一个计数，以及针对字符串值的切片表达式，从而实现快速读取。

	Reader值实现高效读取的关键就在于它内部的已读计数。计数的值就代表着下一次读取的起始索引位置。它可以很容易地被计算出来。Reader值的Seek方法可以直接设定该值中的已读计数值。






*/
func main() {

	var builder strings.Builder

	fmt.Println("Grow the builder ...")
	// Grow方法还可能什么都不做。这种情况的前提条件是：当前的内容容器中的未用容量已经够用了，即：未用容量大于或等于n。
	builder.Grow(10)

	fmt.Printf("The length of contents in the builder is %d.\n", builder.Len())

	fmt.Println("Reset the builder ...")

	// Builder值是可以被重用的。通过调用它的Reset方法，可以让Builder值重新回到零值状态，就像它从未被使用过那样。
	builder.Reset()
	fmt.Printf("The third output(%d):\n%q\n", builder.Len(), builder.String())

	/*
		Reader值拥有的大部分用于读取的方法都会及时地更新已读计数。

			1.ReadByte方法会在读取成功后将这个计数的值加1。

			2.ReadRune方法在读取成功之后，会把被读取的字符所占用的字节数作为计数的增量。

			3.ReadAt方法算是一个例外。它既不会依据已读计数进行读取，也不会在读取后更新它。正因为如此，这个方法可以自由地读取其所属的Reader值中的任何内容。

			4.Reader值的Seek方法也会更新该值的已读计数。实际上，这个Seek方法的主要作用正是设定下一次读取的起始索引位置。


	*/
	var reader strings.Reader

	readingIndex :=reader.Size() - int64(reader.Len())

	fmt.Println("reader index start index ",readingIndex)


}
